
#include <stdint.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <getopt.h>
#include <fcntl.h>
#include <string.h>
#include <sys/ioctl.h>
#include <linux/types.h>
#include <linux/spi/spidev.h>
#include "sc1161.h"
#include "main.h"
#include "esn.h"

static int spi_set_cs_value(int value)
{
   	int cs_mode = 0;
   	int ret = -1;

   	int fd = open(DEV_NAME, O_RDWR);
	if (fd < 0)
	{
		close(fd);
		return FALSE;
	}

    cs_mode = value;
   		
  	if((cs_mode == chip_selected)|| (cs_mode == chip_release)){
		ret = ioctl(fd, SPI_IOC_CS_MODE, &cs_mode);
    	if(ret < 0)
		{
        	goto set_cs_fail;
    	}
   	} 
	else
	{
		goto set_cs_fail;
   	}

set_cs_fail:
	close(fd);
   	return FALSE;
}

static char  CalEsamLrc(char *buf,int len)
{
	int  i;
	char LRC = 0;
	
	for(i=0;i<len;i++)
	{
		LRC ^= buf[i];
	}
	
	LRC = ~LRC;
	
	return LRC;
}

static int sc1161_write(const char *buf,unsigned int len)
{
   int nRet;
   int W_Len = len + 1;
   char send_data[MAX_BUF_LEN] = {0};
   
   send_data[0] = READ_WRITE_FLAG;
   memcpy(&send_data[1],buf,len);

   spi_set_cs_value(chip_selected);

   // write 
   nRet = transfer(&send_data[0], NULL, W_Len);
   if(nRet == FALSE){
	   goto write_fail;
   }

write_fail:
   spi_set_cs_value(chip_release);
   return nRet;
}

//读随机数cmd
static int  ReadRandomCommandToEsam(void)
{
	TypeEsamNormalCommand Frame;
	memset(&Frame,0,sizeof(Frame));
	Frame.CLA	= 0x00;
	Frame.INS	= 0x84;
	Frame.P1	= 0x00;
	Frame.P2	= 0x08;
	Frame.Len1 	= 0x00;
	Frame.Len2 	= 0x00;
	
	Frame.LRC1 = CalEsamLrc((char *)&Frame,sizeof(Frame)-1);	// LRC不算

	sc1161_write((char *)&Frame,sizeof(Frame));
	return 0;
}

static int sc1161_read(char *buf, unsigned int len)
{
	int nRet,times = 0;
	char rec_data[MAX_BUF_LEN]= {0};
	int data_len = 0;
	char lrc = 0;

	spi_set_cs_value(chip_selected);
	
	while(times < 50 && (rec_data[0] != 0x55 ) )
	{
		nRet = transfer(NULL, &rec_data[0], 1);
		if(nRet == FALSE){
			goto read_fail;
		}
			
		times++;
	}
	
	if(rec_data[0] == READ_WRITE_FLAG){               // 0x55 
		nRet = transfer(NULL, &rec_data[1], ESAM_HEAD_LEN);
		if(nRet == FALSE){
			goto read_fail;
		}
		if(SW1_SUCCESS == rec_data[1] && SW2_SUCCESS == rec_data[2]){    // sw1 sw2 
			data_len = (int)((rec_data[3]<<8) + rec_data[4]);  // data len
			if(data_len >= 0){
				nRet = transfer(NULL, &rec_data[5], (data_len+ESAM_LRC_LEN +ESAM_HEAD_LEN));
				if(nRet == FALSE){
					goto read_fail;
				}

				// calculate lrc
				lrc = CalEsamLrc(&rec_data[1],data_len + ESAM_HEAD_LEN);	//(SW1 SW2 LEN1 LEN2 DATA)=>lrc
				if(rec_data[1 + ESAM_HEAD_LEN +  data_len] != lrc){     // 0X55 SW1 SW2 LEN1 LEN2 DATA LRC
					goto read_fail;
				}
				memcpy(buf,&rec_data[1],ESAM_HEAD_LEN+data_len+ESAM_LRC_LEN); // drop 0x55 

			}
		}

	}
	 
read_fail:
	spi_set_cs_value(chip_release);
	return nRet;

}

static int ReadRandomData()
{
	TypeEsamReceData RandomData_st;
    int data_len = 0;
	char RxBuf[MAX_BUF_LEN];
    
	memset(RxBuf,0,sizeof(RxBuf));
    sc1161_read(RxBuf,1000);
	
	RandomData_st.SW1 = RxBuf[0];
	RandomData_st.SW2 = RxBuf[1];
	RandomData_st.Len1 = RxBuf[2];
	RandomData_st.Len2 = RxBuf[3];
	data_len = (int)((RandomData_st.Len1<<8) + RandomData_st.Len2);  // data len
	RandomData_st.Data = &RxBuf[4];
	RandomData_st.LRC2 = RxBuf[ESAM_HEAD_LEN+data_len];
	
	if(RandomData_st.Data[0] == 0)
		return FALSE;
	
	return TRUE;
}

encrypt_type sc1161_prob(void)
{
    int fd = -1;
	int nRet = -1;
    encrypt_type encrypt_sc1161;
   
    fd = spi_config(); // spi init
    if(fd == FALSE){
		close(fd);
		return FALSE;
    }

    // 后期可统一替换成获取 chip version
    nRet = ReadRandomCommandToEsam();
    nRet = ReadRandomData();
    if(nRet == TRUE)
    {
	    encrypt_sc1161 = sc1161y;
    }
    else
    {
	    encrypt_sc1161 = Invaild_type;
    }
	
	close(fd);
    return encrypt_sc1161;
}

/**
 * encrypt_write
 * 写接口
 * data:计算的字符串写入数据结构体
 * 返回写得结果
 */
static int m_encrypt_write(SEND_DATA data)
{
    uint8_t buff[MAX_BUF_LEN];
    int len =0;
    memset(buff,0,sizeof(buff));
    buff[len++]	= data.CLA;
	buff[len++]	= data.INS;
	buff[len++]	= data.P1;
	buff[len++]	= data.P2;
	buff[len++] = data.Len1;
	buff[len++] = data.Len2;
    uint16_t data_len =0;
    data_len <<=8;
    data_len += data.Len1;
    data_len <<=8;
    data_len += data.Len2;
    if (data_len>0)
    {
        memcpy(&buff[len],data.Data,data_len);len+=data_len;
    }
    buff[len++] = CalEsamLrc(buff,len);

    return sc1161_write(buff,len);
}

/**
 * encrypt_read
 * 读接口
 * data:计算的字符串读取数据结构体
 * 返回读取结果
 */
static int m_encrypt_read(uint8_t data[])
{
	int ret = FALSE;
    uint8_t buff[MAX_BUF_LEN];
    memset(buff,0,sizeof(buff));

    sc1161_read(buff,MAX_BUF_LEN);
    uint16_t data_len =0;
    data_len <<=8;  //
    data_len += buff[2];
    data_len <<=8;
    data_len += buff[3];

    if (data_len>0)
    {
        memcpy(data,&buff[4],data_len);
    }
	if (data_len > 0)
	{
		return data_len;
	}
	else if (buff[0] == 0x90 && buff[1] == 0x00)
	{
		ret = TRUE;
	}
    return ret;
}

/**
 * 获取芯片序列号
 * @parm serial_num[]芯片序列号数组
 * return 序列号长度
 */
int sc1161_get_serialnum(uint8_t serial_num[])
{
    SEND_DATA send;
    send.CLA       = 0x00;
    send.INS       = 0xb0;
    send.P1        = 0x99;
    send.P2        = 0x05;
    send.Len1      = 0x00;
    send.Len2      = 0x02;
    send.Data[0]   = 0x00;
    send.Data[1]   = 0x08;
    m_encrypt_write(send);

    return m_encrypt_read(serial_num);
}

/**
  * @brief  将一个十六进制字节串转换成ASCII码表示的十六进制字符串
  * @param  pHex    十六进制数字节串首地址
  * @retval 整数
  */
static void hex_to_str(unsigned char *pHex, unsigned char *pAscii, int nLen)
{
    unsigned char Nibble[2];
    unsigned int i,j;
    for (i = 0; i < nLen; i++){
        Nibble[0] = (pHex[i] & 0xF0) >> 4;
        Nibble[1] = pHex[i] & 0x0F;
        for (j = 0; j < 2; j++){
            if (Nibble[j] < 10){            
                Nibble[j] += 0x30;
            }
            else{
                if (Nibble[j] < 16)
                    Nibble[j] = Nibble[j] - 10 + 'A';
            }
            *pAscii++ = Nibble[j];
        }
    }
}

int get_sc1161_serial_number(char *serial_string)
{
	uint8_t serial_num[32] = {0};
	int ret = sc1161_get_serialnum(serial_num);
	if (ret > 0)
	{
		hex_to_str(serial_num, serial_string, ret);
	}
	return ret;
}


